package com.aleyn.processor.scanner

import com.aleyn.annotation.Factory
import com.aleyn.annotation.InParam
import com.aleyn.annotation.Qualifier
import com.aleyn.annotation.Singleton
import com.aleyn.processor.data.ConstructorParameter
import com.aleyn.processor.data.DefinitionAnnotation
import com.aleyn.processor.data.DependencyKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSValueArgument
import com.google.devtools.ksp.symbol.KSValueParameter
import java.util.Locale

/**
 * @author : Aleyn
 * @date : 2023/07/27 : 22:01
 *
 * 提供在 KSP（Kotlin Symbol Processing）扫描阶段所需的通用工具函数和常量。
 * 这些工具函数主要用于简化从 KSP 符号中提取注解信息、解析参数和处理类型等常见操作。
 */

/**
 * `@Singleton` 注解的元数据表示。
 * `DefinitionAnnotation` 封装了注解的名称和类型，用于在处理器内部进行统一管理。
 */
val SINGLETON = DefinitionAnnotation("lRouterSingle", Singleton::class)

/**
 * `@Factory` 注解的元数据表示。
 * `DefinitionAnnotation` 封装了注解的名称和类型，用于在处理器内部进行统一管理。
 */
val FACTORY = DefinitionAnnotation("lRouterFactory", Factory::class)

/**
 * 所有依赖注入定义注解（如 `@Singleton`, `@Factory`）的元数据列表。
 * 这个列表用于后续的注解过滤和识别。
 */
val DEFINITION_ANNOTATION_LIST = listOf(SINGLETON, FACTORY)

/**
 * 所有依赖注入定义注解的名称列表（小写形式）。
 * 预先转换为小写，用于在过滤时进行不区分大小写的快速比较，提高效率。
 */
val DEFINITION_ANNOTATION_LIST_NAMES =
    DEFINITION_ANNOTATION_LIST.map { it.annotationName?.lowercase(Locale.getDefault()) }

/**
 * 扩展函数，用于过滤注解序列，只保留有效的依赖注入定义注解（`@Singleton`, `@Factory`）。
 * 它通过检查注解的短名称是否存在于 `DEFINITION_ANNOTATION_LIST_NAMES` 列表中来实现过滤。
 */
fun Sequence<KSAnnotation>.filterValidAnn() = this.filter {
    it.shortName.asString().lowercase(Locale.getDefault()) in DEFINITION_ANNOTATION_LIST_NAMES
}

/**
 * 从注解的参数列表 (`List<KSValueArgument>`) 中安全地获取指定名称的参数值。
 *
 * @param T 参数值的预期类型。
 * @param name 要获取的参数名称。在许多注解中，如果只有一个参数，通常默认为 "value"。
 * @return 返回找到并成功转换类型的参数值，如果找不到对应名称的参数或类型不匹配，则返回 `null`。
 */
@Suppress("UNCHECKED_CAST")
internal fun <T> List<KSValueArgument>.getArgValue(name: String = "value") =
    firstOrNull { it.name?.asString() == name }?.value as? T

/**
 * 扩展函数，用于从一个被注解的元素 (`KSAnnotated`) 上获取 `@Qualifier` 注解的值。
 * 限定符（Qualifier）用于在存在多个相同类型的依赖时，精确指定需要注入哪一个。
 *
 * @return 如果找到了 `@Qualifier` 注解，则返回其 `value` 字符串；否则返回 `null`。
 * @throws IllegalStateException 如果 `@Qualifier` 注解存在但其 `value` 为 `null`。
 */
fun KSAnnotated.getQualifier(): String? {
    return annotations.firstOrNull {
        it.shortName.asString() == Qualifier::class.simpleName
    }?.run {
        arguments.getArgValue<String>() ?: error("@Qualifier value is null")
    }
}

/**
 * 解析构造函数的参数，并将其转换为 LRouter 内部的 `ConstructorParameter` 元数据。
 *
 * 这个函数是实现构造函数注入的核心逻辑之一。它负责分析一个构造函数参数的类型和注解，
 * 以此来判断该参数应该如何被满足。具体逻辑如下：
 * - 如果参数被 `@InParam` 注解：表示这是一个需要从 `ViewModel` 的 `SavedStateHandle` 中获取的参数，
 *   对应生成的代码会是 `savedStateHandle.get("key")`。
 * - 如果参数被 `@Qualifier` 注解：表示这是一个带有特定限定符的依赖，需要从 DI 容器中通过 `get(qualifier = ...)` 获取。
 * - 如果参数是 `List<T>` 类型：表示需要获取该类型的所有实例，对应生成的代码是 `getAll<T>()`。
 * - 其他情况：表示这是一个普通的依赖，需要从 DI 容器中通过 `get<T>()` 获取。
 *
 * @param param 要解析的 KSP 构造函数参数 (`KSValueParameter`)。
 * @return [ConstructorParameter] 一个封装了参数注入信息的元数据对象。
 */
fun getConstructorParameter(param: KSValueParameter): ConstructorParameter {
    val firstAnnotation = param.annotations.firstOrNull()
    val annotationName = firstAnnotation?.shortName?.asString()
    val value = firstAnnotation?.arguments?.getArgValue<String>()
    val resolvedType = param.type.resolve()
    val isNullable = resolvedType.isMarkedNullable
    val resolvedTypeString = resolvedType.toString()

    val isList = resolvedTypeString.startsWith("List<")

    return when (annotationName) {
        // @InParam 注解：表示从 ViewModel 的 SavedStateHandle 中注入参数
        InParam::class.simpleName -> ConstructorParameter.ParameterInject(isNullable)
        // @Qualifier 注解：表示带有限定符的依赖注入
        Qualifier::class.simpleName -> ConstructorParameter.Dependency(value, isNullable)
        else -> {
            // List<T> 类型：表示注入一个类型的所有实例 (getAll)
            if (isList) ConstructorParameter.Dependency(kind = DependencyKind.List)
            // 其他情况：表示普通的依赖注入 (get)
            else ConstructorParameter.Dependency(isNullable = isNullable)
        }
    }
}